package com.wingstudio.springcloud.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.params.SetParams;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @Author ITcz
 * @Data 2021-04-30 - 11:01
 */

@Service
public class RedisLockService {
    @Autowired
    JedisPool jedisPool;

    /**
     * 锁释放成功返回值
     * jedis.eval()方法返回的Object其实是long型，如果用Integer类型的1比较结果为false
     */
    private static final Long RELEASE_SUCCESS = 1L;

    /**
     * 加锁成功返回值
     */
    private static final String LOCK_SUCCESS = "OK";

    /**
     * 给锁续时成功返回值
     */
    private static final Long EXPAND_SUCCESS = 1L;


    /**
     * 锁过期时间
     */
    protected long internalLockLeaseTime = 10000;

    /**
     * 获取锁的超时时间
     */
    private long timeout = 1000;

    public String get(String realKey){
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String s = jedis.get(realKey);
            return s;
        }finally {
            jedis.close();
        }
    }

    /**
     * 加锁
     *
     * @param realKey  业务名+":"+数据在数据库中的主键
     * @param value
     * @return
     */
    public boolean lock(String realKey, String value, long lockTime) {
        Jedis jedis = null;
        Long start = System.currentTimeMillis();
        try {
            jedis = jedisPool.getResource();
            //设置set命令的参数
            SetParams params = SetParams.setParams().nx().px(lockTime * 1000);
            for (; ; ) {
//               SET命令返回OK ，则证明获取锁成功
                String result = jedis.set(realKey, value, params);
                if (LOCK_SUCCESS.equals(result)) {
                    return true;
                }
                //否则循环等待，在timeout时间内仍未获取到锁，则获取失败
                long l = System.currentTimeMillis() - start;
                if (l >= timeout) {
                    return false;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } finally {
            jedis.close();
        }
    }

    /**
     * 加锁, 默认过期为5s
     *
     * @param realKey   一般是业务名:数据在数据库中的主键
     * @return
     */
    public boolean lock(String realKey, String value) {
        Jedis jedis = null;
        Long start = System.currentTimeMillis();
        try {
            jedis = jedisPool.getResource();
            //设置set命令的参数
            SetParams params = SetParams.setParams().nx().px(internalLockLeaseTime);
            for (; ; ) {
//               SET命令返回OK ，则证明获取锁成功
                String result = jedis.set(realKey, value, params);
                if (LOCK_SUCCESS.equals(result)) {
                    return true;
                }
                //否则循环等待，在timeout时间内仍未获取到锁，则获取失败
                long l = System.currentTimeMillis() - start;
                if (l >= timeout) {
                    return false;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } finally {
            jedis.close();
        }
    }

    /**
     * 释放分布式锁
     * @param realKey key
     * @param requestId 请求标识
     * @return 是否释放成功
     */
    public boolean releaseDistributedLock(String realKey, String requestId) {
        Jedis jedis = null;
        //使参数KEYS[1]赋值为lockKey，ARGV[1]赋值为requestId。eval()方法是将Lua代码交给Redis服务端执行。Lua脚本
        String script =
                "if redis.call('get',KEYS[1]) == ARGV[1] then" +
                        "   return redis.call('del',KEYS[1]) " +
                        "else" +
                        "   return 0 " +
                        "end";
        try {
            jedis = jedisPool.getResource();
            Object result = jedis.eval(script, Collections.singletonList(realKey),
                    Collections.singletonList(requestId));
            if (RELEASE_SUCCESS.equals(result.toString())) {
                return true;
            }
            return false;
        } finally {
            jedis.close();
        }
    }

    /**
     * 对锁进行续时
     */
    public int expandLockTime(String realKey, String requestId, int lockTime) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] " +
                    "then return redis.call('expire', KEYS[1],ARGV[2]) " +
                    "else return '0' end";
            List<String> list = new ArrayList();
            list.add(requestId);
            list.add(Integer.toString(lockTime));
            //返回值为long型
            Object result = jedis.eval(script, Collections.singletonList(realKey),
                    list);
            if (EXPAND_SUCCESS == (long) result) {
                return 1;
            }
            return 0;
        }finally {
            jedis.close();
        }
    }




}

